package com.heima.article.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.heima.article.mapper.ApArticleConfigMapper;
import com.heima.article.mapper.ApArticleContentMapper;
import com.heima.article.mapper.ApArticleMapper;
import com.heima.article.mapper.ApCollectionMapper;
import com.heima.article.mapper.AuthorMapper;
import com.heima.article.service.ApArticleService;
import com.heima.feigns.behavior.BehaviorFeign;
import com.heima.feigns.user.UserFeign;
import com.heima.model.article.dtos.ArticleDto;
import com.heima.model.article.dtos.ArticleHomeDto;
import com.heima.model.article.dtos.ArticleInfoDto;
import com.heima.model.article.pojos.ApArticle;
import com.heima.model.article.pojos.ApArticleConfig;
import com.heima.model.article.pojos.ApArticleContent;
import com.heima.model.article.pojos.ApAuthor;
import com.heima.model.article.pojos.ApCollection;
import com.heima.model.article.vos.HotArticleVo;
import com.heima.model.behavior.dtos.ApArticleRelationDto;
import com.heima.model.common.constants.ArticleConstants;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.common.mess.ArticleVisitStreamMess;
import com.heima.model.user.pojos.ApUser;
import com.heima.model.user.pojos.ApUserFollow;
import com.heima.utils.threadlocal.AppThreadLocalUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;


@Service
@Slf4j
public class ApArticleServiceImpl extends ServiceImpl<ApArticleMapper, ApArticle> implements ApArticleService {

    @Autowired
    AuthorMapper authorMapper;

    @Autowired
    ApArticleConfigMapper apArticleConfigMapper;

    @Autowired
    ApArticleContentMapper apArticleContentMapper;

    @Autowired
    ApArticleMapper apArticleMapper;

    @Value("${file.oss.web-site}")
    private String webSite;

    @Autowired
    UserFeign userFeign;

    @Autowired
    BehaviorFeign behaviorFeign;

    @Autowired
    ApCollectionMapper apCollectionMapper;

    @Autowired
    RedisTemplate redisTemplate;

    /**
     * 根据参数加载文章列表  v2
     * @param loadtype
     * @param dto
     * @param firstPage
     * @return
     */
    @Override
    public ResponseResult load2(Short loadtype, ArticleHomeDto dto, boolean firstPage) {

        if (firstPage) {
            String hotArticleList = (String) redisTemplate.opsForValue().get(ArticleConstants.HOT_ARTICLE_FIRST_PAGE + dto.getTag());
            List<HotArticleVo> hotArticleVos = JSON.parseArray(hotArticleList, HotArticleVo.class);
            ResponseResult result = ResponseResult.okResult(hotArticleVos);
            result.setHost(webSite);
            return result;
        }

        return load(loadtype, dto);
    }

    /**
     * 更新数据库文章行为数据，并且更新redis
     * @param mess
     */
    @Override
    public void updateApArticle(ArticleVisitStreamMess mess) {

        Long articleId = mess.getArticleId();
        ApArticle apArticle = getById(articleId);
        if (apArticle == null) {
            log.info("updateApArticle getbyId ApArticle is null, id:{}", articleId);
            return;
        }
        //1 更新数据库 **
        if (mess.getView() != 0) {
            int view = (int) (apArticle.getViews() == null ? mess.getView() : mess.getView() + apArticle.getViews());
            apArticle.setViews(view);
        }
        if (mess.getLike() != 0) {
            int like = (int) (apArticle.getLikes() == null ? mess.getLike() : mess.getLike() + apArticle.getLikes());
            apArticle.setLikes(like);
        }
        if (mess.getComment() != 0) {
            int comment = (int) (apArticle.getComment() == null ? mess.getComment() : mess.getComment() + apArticle.getComment());
            apArticle.setComment(comment);
        }
        if (mess.getCollect() != 0) {
            int collection = (int) (apArticle.getCollection() == null ? mess.getCollect() : mess.getCollect() + apArticle.getCollection());
            apArticle.setCollection(collection);
        }
        updateById(apArticle);

        //2 计算文章分值
       int score =  computeScore(apArticle);
       score = score * ArticleConstants.HOT_ARTICLE_SCORE;


        //3 更新redis数据
        //3.1 更新频道下的热点文章数据
        String channelHotArticles = (String) redisTemplate.opsForValue().get(ArticleConstants.HOT_ARTICLE_FIRST_PAGE + apArticle.getChannelId());
        updateArticleCache(apArticle, score, channelHotArticles, ArticleConstants.HOT_ARTICLE_FIRST_PAGE + apArticle.getChannelId());


        //3.2 更新推荐热点文章数据
        String allHotArticles = (String) redisTemplate.opsForValue().get(ArticleConstants.HOT_ARTICLE_FIRST_PAGE + ArticleConstants.DEFAULT_TAG);
        updateArticleCache(apArticle, score, allHotArticles, ArticleConstants.HOT_ARTICLE_FIRST_PAGE + ArticleConstants.DEFAULT_TAG);

    }

    /**
     * 更新redis热点文章
     * @param apArticle 当前文章
     * @param score  分值
     * @param hotArticles redis缓存中热点文章
     * @param tag redis key
     */
    private void updateArticleCache(ApArticle apArticle, int score, String hotArticles, String tag) {

        if (StringUtils.isNotBlank(hotArticles)) {
            // 1 获取热点数据的列表
            List<HotArticleVo> hotArticleVos = JSON.parseArray(hotArticles, HotArticleVo.class);

            // 2 如果当前缓存中有当前文章，更新分值
            boolean flag = false;
            for (HotArticleVo hotArticleVo : hotArticleVos) {
                if (apArticle.getId().equals(hotArticleVo.getId())) {
                    flag = true;
                    hotArticleVo.setScore(hotArticleVo.getScore() + score);
                }
            }

            // 3 缓存中没有当前文章
            if (!flag) {

                HotArticleVo articleVo = new HotArticleVo();
                BeanUtils.copyProperties(apArticle, articleVo);
                articleVo.setScore(score);

                if (hotArticleVos.size() >= 30) {
                    // 4 缓存频道下的最后一条文章的分值小于当前文章分值 则替换 redis 已经排好序
                    HotArticleVo lastHotArticleVo = hotArticleVos.get(hotArticleVos.size() - 1);
                    if (score > lastHotArticleVo.getScore()) {// 替换 90 87 32 43 40    88  先替换 重新排序

                        hotArticleVos.remove(lastHotArticleVo); // 移除最后一条文章

                        hotArticleVos.add(articleVo);  // 新增
                    }

                }else { // 直接新增
                    hotArticleVos.add(articleVo);
                }
            }

            //5 排序
            hotArticleVos = hotArticleVos.stream().sorted(Comparator.comparing(HotArticleVo::getScore)
                    .reversed()).collect(Collectors.toList());

            //6 保存到redis
            redisTemplate.opsForValue().set(tag, JSON.toJSONString(hotArticleVos));
            log.info("updateApArticle updateArticleCache success");
        }
    }


    /**
     * 计算文章分值方法     阅读：1，点赞：3，评论：5，收藏：8
     * @param apArticle
     * @return
     */
    private int computeScore(ApArticle apArticle) {

        int score = 0;

        if (apArticle.getViews() != null) {
            score += apArticle.getViews().intValue() * ArticleConstants.HOT_ARTICLE_VIEW_WEIGHT;
        }

        // 点赞 3
        if (apArticle.getLikes() != null) {
            score += apArticle.getLikes() * ArticleConstants.HOT_ARTICLE_LIKE_WEIGHT;
        }
        // 评论 5
        if (apArticle.getComment() != null) {
            score += apArticle.getComment() * ArticleConstants.HOT_ARTICLE_COMMENT_WEIGHT;
        }
        // 收藏 8
        if (apArticle.getCollection() != null) {
            score += apArticle.getCollection() * ArticleConstants.HOT_ARTICLE_COLLECTION_WEIGHT;
        }

        return score;
    }

    /**
     * 加载文章详情的初始化配置信息，比如关注、喜欢、不喜欢等
     * @param dto
     * @return {"isfollow":true,"islike":true,"isunlike":false,"iscollection":true}
     */
    @Override
    public ResponseResult loadArticleBehavior(ArticleInfoDto dto) {
        //1 参数检查
        if (dto == null || dto.getArticleId() == null || dto.getAuthorId() == null) {
            return ResponseResult.okResult(new HashMap<>());
        }
        boolean iscollection=false,isfollow=false;

        //2 查询是否关注
        ApUser user = AppThreadLocalUtils.getUser();
        if (user == null) {
            return ResponseResult.okResult(new HashMap<>());
        }
        // 查询作者对应的Apuser主键
        ApAuthor apAuthor = authorMapper.selectById(dto.getAuthorId());
        if (apAuthor == null) {
            return ResponseResult.okResult(new HashMap<>());
        }

        ApUserFollow userFollow = userFeign.findByUserIdAndFollowId(user.getId(), apAuthor.getUserId());
        if (userFollow != null) {
            isfollow = true;
        }

        //3 查询是否不喜欢/点赞
        ApArticleRelationDto apDto = new ApArticleRelationDto();
        apDto.setArticleId(dto.getArticleId());
        apDto.setEntryId(user.getId());  // 用户id
        apDto.setType((short) 1);
        Map map = behaviorFeign.findApArticleRelation(apDto);

        //4 查询是否收藏
        if (map.get("entryId") != null) {
            ApCollection apCollection = apCollectionMapper.selectOne(Wrappers.<ApCollection>lambdaQuery()
                    .eq(ApCollection::getArticleId,dto.getArticleId())
                    .eq(ApCollection::getEntryId, map.get("entryId") )
                    .eq(ApCollection::getType, ApCollection.Type.ARTICLE.getCode() )
            );

            if (apCollection != null) {
                iscollection = true;
            }
        }

        //5 封装结果返回
        Map<String, Object> resultMap = new HashMap<>();
        resultMap.put("iscollection", iscollection);
        resultMap.put("isfollow", isfollow);
//        resultMap.put("islike", map.get("islike"));
//        resultMap.put("isunlike", map.get("isunlike"));
        resultMap.putAll(map);

        return ResponseResult.okResult(resultMap);
    }

    /**
     * 根据文章id查询文章内容
     * @param dto
     * @return
     */
    @Override
    public ResponseResult loadArticleInfo(ArticleInfoDto dto) {

        if (dto == null || dto.getArticleId() == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_REQUIRE);
        }

        // 查询文章
        ApArticleConfig config = apArticleConfigMapper.selectOne(Wrappers
                .<ApArticleConfig>lambdaQuery()
                .eq(ApArticleConfig::getArticleId, dto.getArticleId()));
        if (config == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST);
        }
        if (config.getIsDown() || config.getIsDelete()) {
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST,"文章已删除或下架");
        }

        ApArticle article = getById(dto.getArticleId());
        if (article == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST, "文章不存在");
        }

        ApArticleContent content = apArticleContentMapper.selectOne(Wrappers.<ApArticleContent>lambdaQuery()
                .eq(ApArticleContent::getArticleId, dto.getArticleId()));
        if (content == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST, "文章不存在");
        }

        Map<String, Object> resultMap = new HashMap<>();
        resultMap.put("config", config);
        resultMap.put("content", content);

        return ResponseResult.okResult(resultMap);
    }

    /**
     * 根据参数加载文章列表
     * @param loadtype 1为加载更多  2为加载最新
     * @param dto
     * @return
     */
    @Override
    public ResponseResult load(Short loadtype, ArticleHomeDto dto) {

        if (dto == null) {
            dto = new ArticleHomeDto();
        }
        // 参数值处理
        if (StringUtils.isBlank(dto.getTag())) {
            dto.setTag(ArticleConstants.DEFAULT_TAG);
        }
        Integer size = dto.getSize();
        if (size == null || size > 50) {
            size = Math.min(size, 50);
        }
        if (size <= 0) {
            size = 10;
        }
        if (dto.getMinBehotTime() == null) {
            dto.setMinBehotTime(new Date());
        }
        if (dto.getMaxBehotTime() == null) {
            dto.setMaxBehotTime(new Date());
        }

        // 类型 1 2
        if (!loadtype.equals(ArticleConstants.LOADTYPE_LOAD_MORE)
                && !loadtype.equals(ArticleConstants.LOADTYPE_LOAD_NEW)) {
            loadtype = ArticleConstants.LOADTYPE_LOAD_MORE;
        }

        List<ApArticle> list = apArticleMapper.loadArticleList(dto, loadtype);

        // 处理图片封面
        ResponseResult result = ResponseResult.okResult(list);
        result.setHost(webSite);

        return result;
    }

    /**
     * 保存文章信息三张表
     *
     * @param articleDto
     * @return 返回文章id主键
     */
    @Override
    public ResponseResult saveArticle(ArticleDto articleDto) {

        ApArticle apArticle = new ApArticle();
        BeanUtils.copyProperties(articleDto, apArticle);

        //1 设置作者id
        String authorName = articleDto.getAuthorName();
        ApAuthor apAuthor = authorMapper.selectOne(Wrappers.<ApAuthor>lambdaQuery().eq(ApAuthor::getName, authorName));
        if (apAuthor == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST, "作者不存在");
        }
        apArticle.setAuthorId(apAuthor.getId().longValue());

        if (apArticle.getId() == null) { // 新增
            //2 补充其他属性Article
            apArticle.setComment(0); // 评论
            apArticle.setViews(0);  // 阅读
            apArticle.setCollection(0); // 收藏
            apArticle.setLikes(0); // 点赞

            //3 保存Article数据到数据库
            save(apArticle);  // 返回apArticleID

            //4 保存ArticleConfig数据到数据库
            ApArticleConfig apArticleConfig = new ApArticleConfig();
            apArticleConfig.setArticleId(apArticle.getId());
            apArticleConfig.setIsForward(true);
            apArticleConfig.setIsComment(true);
            apArticleConfig.setIsDown(false);
            apArticleConfig.setIsDelete(false);
            apArticleConfigMapper.insert(apArticleConfig);

            //5 保存ArticleContent数据到数据库
            ApArticleContent apArticleContent = new ApArticleContent();
            apArticleContent.setArticleId(apArticle.getId());
            apArticleContent.setContent(articleDto.getContent());
            apArticleContentMapper.insert(apArticleContent);

        } else { // 修改

            // 先查询后改
            ApArticle article = getById(apArticle.getId());
            if (article == null) {
                log.info("ApArticle getById is null: {}", articleDto.getId());
                return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST, "文章不存在");
            }
            // 修改 apArticle****
            updateById(apArticle);

            // 文章内容修改
            ApArticleContent articleContent = apArticleContentMapper.selectOne(Wrappers.<ApArticleContent>lambdaQuery().eq(ApArticleContent::getArticleId, apArticle.getId()));
            if (articleContent == null) {
                log.info("articleContent  is null: {}", articleDto.getId());
                return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST, "文章内容不存在");
            }
            articleContent.setContent(articleDto.getContent());
            apArticleContentMapper.updateById(articleContent);
        }


        //6 封装返回结果id
        return ResponseResult.okResult(apArticle.getId());
    }
}
